package name.cyrax.red.jpv.impl.funcs.core;

import name.cyrax.red.jpv.api.adapters.DoubleAdapter;
import name.cyrax.red.jpv.api.adapters.IntegerAdapter;
import name.cyrax.red.jpv.api.adapters.LongAdapter;
import name.cyrax.red.jpv.api.adapters.NumericAdapter;
import name.cyrax.red.jpv.api.funcs.FunctorInstance;
import name.cyrax.red.jpv.api.funcs.Invocable;
import name.cyrax.red.jpv.api.funcs.UnaryFunctor;
import name.cyrax.red.jpv.api.funcs.info.FunctorMethod;
import name.cyrax.red.jpv.api.funcs.info.ValueType;
import name.cyrax.red.jpv.impl.adapters.DoubleImpl;
import name.cyrax.red.jpv.impl.adapters.IntegerImpl;
import name.cyrax.red.jpv.impl.adapters.LongImpl;

public abstract class FunctorUnaryImpl extends FunctorAbstractUnaryImpl<NumericAdapter> implements UnaryFunctor
{
	private static final Class<?> OP_METHOD_CLASS = FunctorUnaryImpl.class; 
	private static final FunctorMethod FUNC_METHOD_INTEGER = FunctorMethod.newMethod(OP_METHOD_CLASS,
		"opInteger", ValueType.INTEGER, ValueType.UNKNOWN);
	private static final FunctorMethod FUNC_METHOD_LONG = FunctorMethod.newMethod(OP_METHOD_CLASS,
		"opLong", ValueType.LONG, ValueType.UNKNOWN);
	private static final FunctorMethod FUNC_METHOD_DOUBLE = FunctorMethod.newMethod(OP_METHOD_CLASS,
		"opDouble", ValueType.DOUBLE, ValueType.UNKNOWN);

	private final FunctorInstance funcInstanceInteger = getFunctorInstance(FUNC_METHOD_INTEGER);
	private final FunctorInstance funcInstanceLong = getFunctorInstance(FUNC_METHOD_LONG);
	private final FunctorInstance funcInstanceDouble = getFunctorInstance(FUNC_METHOD_DOUBLE);
	
	@Override public NumericAdapter newAdapter(final IntegerAdapter p0) {
		return newAdapter(funcInstanceInteger, p0);
	}
	@Override public NumericAdapter newAdapter(final LongAdapter p0) {
		return newAdapter(funcInstanceLong, p0);
	}
	@Override public NumericAdapter newAdapter(final DoubleAdapter p0) {
		return newAdapter(funcInstanceDouble, p0);
	}

	public abstract int opInteger(int v0); 
	public abstract long opLong(long v0);
	public abstract double opDouble(double v0);

	NumericAdapter newAdapter(final FunctorInstance fi, final IntegerAdapter p0) {
		return checkAdapterType(new IntegerImpl() {
			@Override public FunctorInstance getFunctorInstance() {
				return fi;
			}
			@Override public NumericAdapter getParam(int index) {
				return switchOf(index, p0);
			}
			@Override public int size() { return p0.size(); }
			@Override public int getInteger(int index) {
				return opInteger(p0.getInteger(index));
			}
		});
	}

	NumericAdapter newAdapter(final FunctorInstance fi, final LongAdapter p0) {
		return checkAdapterType(new LongImpl() {
			@Override public FunctorInstance getFunctorInstance() {
				return fi;
			}
			@Override public NumericAdapter getParam(int index) {
				return switchOf(index, p0);
			}
			@Override public int size() { return p0.size(); }
			@Override public long getLong(int index) {
				return opLong(p0.getLong(index));
			}
		});
	}
	NumericAdapter newAdapter(final FunctorInstance fi, final DoubleAdapter p0) {
		return checkAdapterType(new DoubleImpl() {
			@Override public FunctorInstance getFunctorInstance() {
				return fi;
			}
			@Override public NumericAdapter getParam(int index) {
				return switchOf(index, p0);
			}
			@Override public int size() { return p0.size(); }
			@Override public double getDouble(int index) {
				return opDouble(p0.getDouble(index));
			}
		});
	}
	
	protected final <V extends Number> Invocable<V> newInvocable0(Invocable<V> v0)
	{
		return new UnaryInvocableImpl<V>(v0);
	}
	
	@Override public <V extends Number> Invocable<V> newInstance(Invocable<V> v0)
	{
		return newInvocable0(v0);
	}
}